# coding=utf-8
"""
读取解析track.json
"""
import json
import os
import sys
import time
import datetime

import arrow
from pymongo import MongoClient

base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "base.settings")

from base.settings import MONGO_ADDR
from common.transaction.model import WITHDRAW_STATUS

# for test
# MG = MongoClient('127.0.0.1:27017').bigbang
# TRACK_FILE_NAME = os.path.join(os.path.dirname(__file__), 'test.json')

MG = MongoClient(MONGO_ADDR).bigbang
TRACK_FILE_NAME = '/var/log/bigbang/track.json'
SERVICE_ID = None  # 不同服务器使用不同的ID, 从sys argv读取
PREVIOUS_TRACK_FILE_NAME = '/var/log/bigbang/track.json.1'


def get_now():
    return arrow.now('+08:00').format('YYYY-MM-DD hh:mm:ss')


def get_file_size():
    max_try = 5
    count = 0
    while count <= max_try:
        try:
            size = os.path.getsize(TRACK_FILE_NAME)
        except OSError as e:
            print e
            count += 1
            time.sleep(0.5)
            continue
        else:
            return size


def save_history(fbytes):
    """
    mongodb写入文件innode和上一次读取到的字节数和时间
    """
    MG.inode.update(
        {"_id": SERVICE_ID},
        {"$set": {"fbytes": fbytes, "updated_at": datetime.datetime.utcnow()}},
        upsert=True
    )


def get_history():
    return MG.inode.find_one({"_id": SERVICE_ID})


def save_user_stats(data):
    user_id = data['user_id']
    insert_data = {}
    insert_data['$set'] = data['set']
    insert_data['$inc'] = data['inc']
    insert_data['$setOnInsert'] = data.get('setOnInsert', {})
    insert_data['$setOnInsert'].update({"created_at": data['updated_at']})
    save_with_retry(MG.user_stats, user_id, insert_data)


def save_activity_stats(data):
    updated_at = data['updated_at']
    date_str = arrow.get(updated_at).to('+08:00').format('YYYY-MM-DD')
    activity_type = data['activity_type']
    key = '%s-%s' % (activity_type, date_str)
    insert_data = {}
    insert_data['$set'] = {'updated_at': updated_at, 'date': date_str, 'activity_type': activity_type}
    insert_data['$setOnInsert'] = {'created_at': updated_at}
    insert_data['$inc'] = data['activity']
    if 'push' in data:
        insert_data['$addToSet'] = data['push']
    save_with_retry(MG.activity_stats, key, insert_data)


def save_daily_stats(data):
    user_id = data['user_id']
    data['set']['user_id'] = user_id
    data['set'].pop('aid', None)
    updated_at = data['updated_at']
    date_str = arrow.get(updated_at).to('+08:00').format('YYYY-MM-DD')
    key = '%s-%s' % (user_id, date_str)
    insert_data = {}
    insert_data['$set'] = data['set']
    insert_data['$set'].update({
        "is_fresh": 0,
    })
    insert_data['$setOnInsert'] = {
        "created_at": data['updated_at']
    }
    try:
        user_stats = MG.user_stats.find_one({"_id": user_id})
    except Exception:
        pass
    else:
        chn = user_stats.get('chn')
        if chn:
            insert_data['$setOnInsert'].update({'chn': chn})
        aid = user_stats.get('aid')
        if aid:
            insert_data['$setOnInsert'].update({'aid': aid})
        register_date = user_stats.get('created_at')
        register_date = arrow.get(register_date).to('+08:00').format('YYYY-MM-DD')
        if register_date == arrow.now('+08:00').format('YYYY-MM-DD'):
            insert_data['$set'].update({'is_fresh': 1})
    insert_data['$inc'] = data['inc']
    save_with_retry(MG.daily_stats, key, insert_data)


def decode(line):
    data = json.loads(line)
    ts = arrow.get(data['@timestamp']).datetime
    activity_type = data.get('activity_type')
    bet_type = data.get('bet_type')
    user_id = data.get('user_id')
    type = data['type']
    to_save = {
        'user_id': user_id,
        'aid': data.get('aid', None),
        'type': type,
        'set': {"updated_at": ts},
        'setOnInsert': {},
        'inc': {},
        'updated_at': ts,
        'activity': {},
        'activity_type': activity_type
    }
    if type == 'register':
        to_save['set']['register_at'] = ts
        to_save['set']['aid'] = data['aid']
        to_save['set']['chn'] = data['chn'] or 'google'
    elif type == 'login' and data['logon'] == False:
        to_save['set']['first_login_at'] = ts
    elif type == 'gain':
        bet_price = float(data['bet_price'])
        win_price = float(data['win_price'])
        bonus = float(data.get('bonus', 0))
        to_save['activity']['daily_pay.total'] = float(bet_price)
        to_save['activity']['daily_pay.count'] = 1
        to_save['activity']['daily_win.total'] = win_price
        to_save['activity']['daily_bonus.total'] = bonus
        to_save['activity']['daily_win.count'] = 1
        to_save['activity']['daily_bonus.count'] = 1
    elif type == 'win':
        win_price = float(data['price'])
        bonus = float(data.get('bonus', 0))
        to_save['inc']['win.total'] = win_price
        to_save['inc']['win.count'] = 1
        to_save['set']['win.last_at'] = ts
        to_save['inc']['bonus.total'] = bonus
        to_save['inc']['bonus.count'] = 1
        to_save['set']['bonus.last_at'] = ts
        to_save['inc']['win.%s' % activity_type] = win_price
        to_save['inc']['bonus.%s' % activity_type] = bonus
        to_save['inc']['%s.%s' % ('gain', activity_type)] = win_price + bonus
        to_save['inc']['gain.total'] = win_price + bonus
        # for activity_stats
        to_save['activity']['win.%s' % bet_type] = win_price
        to_save['activity']['win.total'] = win_price
        to_save['activity']['bonus.%s' % bet_type] = bonus
        to_save['activity']['bonus.total'] = bonus
        to_save['activity']['win.count'] = 1
        to_save['activity']['bonus.count'] = 1
        to_save['push'] = {'win.{}_users'.format(bet_type): user_id, 'win.users': user_id}
    elif type == 'withdraw' and data['action'] == 'submit':
        to_save['inc']['%s.%s' % ('withdraw', data['withdraw_type'])] = float(data['cash_price'])
        if int(data['status']) == WITHDRAW_STATUS.WAIT:
            to_save['inc']['withdraw.total_wait'] = float(data['cash_price'])
            if float(data['cash_price']) > 0:
                to_save['inc']['withdraw.count_wait'] = 1
            else:
                to_save['inc']['withdraw.count_wait'] = -1
        if int(data['status']) == WITHDRAW_STATUS.FORBIDDEN:
            to_save['inc']['withdraw.total_forbidden'] = float(data['cash_price'])
            if float(data['cash_price']) > 0:
                to_save['inc']['withdraw.count_forbidden'] = 1
            else:
                to_save['inc']['withdraw.count_forbidden'] = -1
        if int(data['status']) == WITHDRAW_STATUS.DONE:
            to_save['inc']['withdraw.total'] = float(data['cash_price'])
            to_save['inc']['withdraw.count'] = 1
        to_save['set']['withdraw.last_at'] = ts
        to_save['inc']['total_gain'] = float(data['cash_price'])
    elif type == 'recharge':
        to_save['inc']['%s.%s' % ('recharge', data['channel'])] = float(data['price'])
        to_save['inc']['recharge.total'] = float(data['price'])
        to_save['inc']['total_gain'] = 0 - float(data['price'])
        to_save['set']['recharge.last_at'] = ts
        to_save['inc']['recharge.count'] = 1
    elif type == 'recharge_bonus':
        to_save['inc']['recharge_bonus.total'] = float(data['price'])
        to_save['inc']['recharge_bonus.count'] = 1
    elif type == 'system_recharge':
        to_save['inc']['system_recharge.total'] = float(data['price'])
        to_save['inc']['system_recharge.count'] = 1
    elif type == 'pay':
        to_save['inc']['pay.count'] = 1
        to_save['inc']['pay.total'] = float(data['price'])
        to_save['inc']['%s.%s' % ('pay', activity_type)] = float(data['price'])
        if data.get('coupon'):
            to_save['inc']['pay.coupon'] = float(data['price'])
            pay_money = float(data['price']) - float(data['coupon'])
            to_save['inc']['pay.money'] = pay_money
            to_save['inc']['%s.%s' % ('gain', activity_type)] = -pay_money
            to_save['inc']['gain.total'] = -pay_money
        else:
            to_save['inc']['pay.money'] = float(data['price'])
            to_save['inc']['%s.%s' % ('gain', activity_type)] = -float(data['price'])
            to_save['inc']['gain.total'] = -float(data['price'])
        # for activity_stats
        to_save['activity']['pay.%s' % bet_type] = float(data['price'])
        to_save['activity']['pay.total'] = float(data['price'])
        to_save['activity']['pay.count'] = 1
        to_save['push'] = {'pay.{}_users'.format(bet_type): user_id, 'pay.users': user_id}
    elif type == 'active':
        to_save['set']['aid'] = data['aid']
        to_save['set']['cvc'] = data['cvc']
        to_save['set']['ip'] = data['ip']
        to_save['set']['ua'] = data['ua']
        to_save['set']['chn'] = data['chn']
    elif type == 'refund':
        to_save['inc']['refund.total'] = float(data['price'])
        to_save['inc']['%s.%s' % ('refund', activity_type)] = float(data['price'])
        to_save['inc']['%s.%s' % ('gain', activity_type)] = float(data['price'])
        # for activity_stats
        to_save['activity']['refund.%s' % bet_type] = float(data['price'])
        to_save['activity']['refund.total'] = float(data['price'])
        to_save['activity']['refund.count'] = 1
        to_save['push'] = {'refund.{}_users'.format(bet_type): user_id, 'refund.users': user_id}
    elif type == 'coupon':
        action = data['action']
        to_save['inc']['coupon.%s' % action] = int(data['price'])
    elif type == 'metis':
        if int(data['is_bank']):
            to_save['set'][type + '.banker.%s_is_banker' % data['game_id']] = 1
            to_save['inc'][type + '.banker.%s_banker_count' % data['game_id']] = 1
            to_save['inc'][type + '.banker.%s_bank_total_bet' % data['game_id']] = float(data['bank_total_bet'])
            to_save['inc'][type + '.banker.%s_bank_bet_result' % data['game_id']] = float(data['bank_bet_result'])
        to_save['inc'][type + '.total_bet'] = float(data['bet_amount'])
        to_save['inc'][type + '.%s_bet_amount' % data['game_id']] = float(data['bet_amount'])
        to_save['inc'][type + '.total_profit'] = float(data['bet_result'])
        to_save['inc'][type + '.%s_total_profit' % data['game_id']] = float(data['bet_result'])
        to_save['set']['updated_at'] = datetime.datetime.strptime(data['create_time'], "%Y-%m-%d %H:%M:%S")
        to_save['updated_at'] = datetime.datetime.strptime(data['create_time'], "%Y-%m-%d %H:%M:%S")
        if type == 'metis':
            to_save['inc']['metis.total_lose'] = float(data['lose_amount'])
            to_save['inc']['metis.%s_total_lose' % data['game_id']] = float(data['lose_amount'])
            to_save['inc']['metis.%s_invalid_amount' % data['game_id']] = float(data['invalid_amount'])
    return to_save


def track_device(data):
    aid = data['set']['aid']
    insert_data = {}
    insert_data['$set'] = data['set']
    insert_data['$setOnInsert'] = {"created_at": data['updated_at']}
    save_with_retry(MG.device_stats, aid, insert_data)


def save_with_retry(db, id, data):
    for k, v in data.items():
        if not v:
            data.pop(k)
    x = db.update({'_id': id}, data, upsert=True)
    print get_now(), x


def handler_line(line):
    try:
        data = decode(line)
        type = data['type']
        if type == 'login':
            save_user_stats(data)
        if type == 'active':
            track_device(data)
            if data['user_id'] != 1:
                data['set'].pop('chn', None)
                data['set'].pop('aid', None)
                save_user_stats(data)
                save_daily_stats(data)
        elif type == 'register':
            track_device(data)
            save_user_stats(data)
        elif type == 'pay':
            save_user_stats(data)
            save_daily_stats(data)
            save_activity_stats(data)
        elif type == 'win':
            save_user_stats(data)
            save_daily_stats(data)
            save_activity_stats(data)
        elif type == 'gain':
            save_activity_stats(data)
        elif type == 'recharge':
            save_user_stats(data)
            save_daily_stats(data)
        elif type == 'recharge_bonus':
            save_user_stats(data)
            save_daily_stats(data)
        elif type == 'system_recharge':
            save_user_stats(data)
            save_daily_stats(data)
        elif type == 'withdraw':
            save_user_stats(data)
            save_daily_stats(data)
        elif type == 'refund':
            save_user_stats(data)
            save_daily_stats(data)
            save_activity_stats(data)
        elif type == 'coupon':
            save_user_stats(data)
            save_daily_stats(data)
        elif type == 'metis' or type == 'hera':
            save_user_stats(data)
            save_daily_stats(data)
    except Exception as e:
        print 'handler line error %s' % e


class Tail(object):
    def __init__(self):
        self.fd = file(TRACK_FILE_NAME, 'rb')
        self.fbytes = 0  # 上次读取到的位置

    def read_all_lines(self):
        self.fd.seek(self.fbytes, 0)
        for line in self.fd.readlines():
            handler_line(line.strip())
            save_history(self.fbytes)
        self.fd = file(TRACK_FILE_NAME, 'rb')
        self.fbytes = 0
        save_history(0)

    def run(self):
        history = get_history()
        if history:
            self.fbytes = history['fbytes']
            if get_file_size() < self.fbytes:
                # 遇到logrotate，先读完track.json.1
                self.fd = file(PREVIOUS_TRACK_FILE_NAME, 'rb')
                self.read_all_lines()
        else:
            save_history(0)
        while 1:
            file_size = get_file_size()
            if file_size < get_history()['fbytes']:
                self.read_all_lines()
            else:
                while 1:
                    self.fd.seek(self.fbytes, 0)
                    line = self.fd.readline()
                    if line:
                        handler_line(line.strip())
                        self.fbytes = self.fd.tell()
                        print self.fbytes
                        save_history(self.fbytes)
                    else:
                        time.sleep(1)
                        break


if __name__ == "__main__":
    SERVICE_ID = int(sys.argv[1])
    tail = Tail()
    tail.run()
